﻿/// <reference path="_namespace.js" />
/// <reference path="/ajax/components/Component.js" />
/// <reference path="GameComponentCollection.js" />
/// <reference path="GameWindow.js"/>
/// <reference path="IGameComponent.js"/>
/// <reference path="IUpdateable.js"/>
/// <reference path="IDrawable.js"/>
/// <reference path="GameServiceContainer.js" />
/// <reference path="IGraphicsDeviceManager.js"/>
/// <reference path="../../effects/Animation.js"/>
/// <reference path="/ajax/drawing/Timer.js"/>
/// <reference path="graphics/IGraphicsDeviceService.js"/>
/// <reference path="graphics/GraphicsDevice.js"/>


/*****************************************************************************
Ajaxna library class definition. Inheriting from ajax.BaseObject.
Author: krobinson 10/14/2008 5:18:06 PM.
Class:  ajax.xna.framework.Game
*****************************************************************************/

$addNamespace("ajax.xna.framework");
$imports("ajax.xna.framework.GameServiceContainer");
$imports("ajax.drawing.Timer");
$imports("ajax.xna.framework.IUpdateable", "ajax.xna.framework.IDrawable");
$imports("ajax.xna.framework.graphics.GraphicsDevice");
$imports("ajax.xna.framework.GameComponentCollection");
$imports("ajax.xna.framework.GameWindow");
$imports("ajax.effects.Animation");
$imports("ajax.windows.WinFormDisplayFlags");
// Just to be nice...
$imports("ajax.drawing.Colour");
$imports("ajax.xna.framework.IGraphicsDeviceManager", "ajax.xna.framework.graphics.IGraphicsDeviceService");

//-----------------------------------------------------------------------------

ajax.xna.framework.Game = function(doc)
{///<param name="doc" type="domDocument">The dom Document the game will display on.</param>
    ///<field name="graphicsDevice" type="ajax.xna.framework.graphics.GraphicsDevice">Gets the current ajax.xna.framework.graphics.GraphicsDevice.</field>


    ajax.xna.framework.Game.superConstructor.apply(this, arguments);
    // Create Components
    this._components = new ajax.xna.framework.GameComponentCollection(this.getDocument());

    // Create loop
    this._loop = new ajax.effects.Animation(this.getDocument(), 0, 0);
    this._loop.callback = Function.createBinding(this, this.tick);

    // Create Game Window
    this._gameWindow = new ajax.xna.framework.GameWindow(doc, "", 640, 480, ajax.windows.WinFormDisplayFlags.NoResize, "Ajaxna Game");
    // Use xna icon
    this._gameWindow.iconFile = ajax.getDomainPath() + "/images/xna.gif";
    this._gameWindow.contentStyle.overflow = "hidden";
    this._gameWindow.add_disposing(Function.createBinding(this, this.exit));
    

    // Start/Stop Loop delegates
    var stopLoopDelegate = Function.createBinding(this, function(e, r)
    {
        ajax.effects.Animation.cast(this._loop).stop();
        ajax.debug.Output.writeLn("Loop stopped.");
    }
    );
    var startLoopDelegate = Function.createBinding(this, function(e, r)
    {
        ajax.effects.Animation.cast(this._loop).start();
        ajax.debug.Output.writeLn("Loop Started!! **");
    }
    );
    // When the game window becomes inactive, stop the animation loop
    this._gameWindow.add_hide(stopLoopDelegate);
    this._gameWindow.add_resizingStart(stopLoopDelegate);

    // When the game window becomes active again, start the animation loop    
    this._gameWindow.add_resizingEnd(startLoopDelegate);
    this._gameWindow.add_show(startLoopDelegate);


    this.services = new ajax.xna.framework.GameServiceContainer();
    this.gameTime = new ajax.drawing.Timer();

    this.graphicsDevice = null;
    this._graphicsDeviceManager = null;
    this._graphicsDeviceService = null;

    // Events
    this.getEvent(this.getWindow(), "onExiting");

}
$extend("ajax.xna.framework.Game", "ajax.components.Component");
if ($getDesignMode())
    ajax.xna.framework.Game.prototype = ajax.components.Component.prototype;
//-----------------------------------------------------------------------------

// Public members
//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.services = ajax.xna.framework.GameServiceContainer.prototype;
ajax.xna.framework.Game.prototype.gameTime = ajax.drawing.Timer.prototype;
ajax.xna.framework.Game.prototype.graphicsDevice = ajax.xna.framework.graphics.GraphicsDevice.prototype;

// Private members
//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype._graphicsDeviceManager = ajax.xna.framework.IGraphicsDeviceManager.prototype;
ajax.xna.framework.Game.prototype._graphicsDeviceService = ajax.xna.framework.graphics.IGraphicsDeviceService.prototype;
ajax.xna.framework.Game.prototype._components       = ajax.xna.framework.GameComponentCollection.prototype;
ajax.xna.framework.Game.prototype._gameWindow       = ajax.xna.framework.GameWindow.prototype;
ajax.xna.framework.Game.prototype._loop             = ajax.effects.Animation.prototype;
ajax.xna.framework.Game.prototype._isMouseVisible   = true;
ajax.xna.framework.Game.prototype._oldMouseCursor   = "";

// Public Accessors
//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.add_exiting = function(handler){ this.getEvent(this.getWindow(), "onExiting").addListener(handler); }
ajax.xna.framework.Game.prototype.remove_exiting = function(handler) { this.getEvent(this.getWindow(), "onExiting").removeListener(handler); }

ajax.xna.framework.Game.prototype.get_components = function() 
{///<returns type="ajax.xna.framework.GameComponentCollection"/> 
    return this._components; 
}
ajax.xna.framework.Game.prototype.get_window     = function()
{///<returns type="ajax.xna.framework.GameWindow"/>
    return this._gameWindow;        
}
ajax.xna.framework.Game.prototype.get_isMouseVisible = function()
{///<returns type="boolean">IE Only. Gets or sets a value indicating whether the mouse cursor should be visible.</value>
    return this._isMouseVisible;
}
ajax.xna.framework.Game.prototype.set_isMouseVisible = function(value)
{///<param name="value" type="boolean">IE Only. Gets or sets a value indicating whether the mouse cursor should be visible.</param>
    this._isMouseVisible = value;
    var content = this._gameWindow.getContent();
    
    var setMouseVisible = function()
    {        
        // As long as Delegate used we assume context is us as normal.
        var style = this._gameWindow.getContent().style;
        // We see the value through closure...
        if (!value)
        {            
            this._oldMouseCursor = style.cursor;
            style.cursor = String.toFormat("url({0})", ajax.getDomain("cursors/blank.cur"));
        }
        else    
            style.cursor = this._oldMouseCursor;
        
        // remove handler - we see the delegate through closure...
        this._gameWindow.remove_load(setMouseVisibleDelegate);        
    }
    
    // In case we need the delegate
    var setMouseVisibleDelegate = Function.createBinding(this, setMouseVisible);
    
    if (!content)
    {
        // content is not yet loaded, we'll have to defer this until it is!
        this._gameWindow.add_load(setMouseVisibleDelegate);        
        return; // We can't set it yet
    }
    
    // Ok we can do this now...
    setMouseVisibleDelegate();           
}
/*
In order to make the API a little less "java", I'm gonna have the graphics device as a simple property, set during the run() method.
ajax.xna.framework.Game.prototype.get_graphicsDevice = function()
{///<returns type="ajax.xna.framework.graphics.GraphicsDevice"/>
    if (this._graphicsDeviceService == null)
        this._graphicsDeviceService = ajax.xna.framework.graphics.IGraphicsDeviceService.cast(this.services.getService(ajax.xna.framework.graphics.IGraphicsDeviceService.getFunctionName()));
    
    return this._graphicsDeviceService.graphicsDevice;    
}
*/

// Public methods
//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.loadContent = function()
{///<summary>Called when graphics resources need to be loaded. Override this method to load any game-specific graphics resources. No need to call base.</summary>
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.initialize = function()
{///<summary>Called after the Game and Graphics.GraphicsDevice are created, but before Game.LoadContent. Call base.</summary>

    // Initialize all our components
    for (var i = 0; i < this._components.get_count(); i++)
        ajax.xna.framework.IGameComponent.cast(this._components[i]).initialize();

    
    if (this._graphicsDeviceService != null && this._graphicsDeviceService.graphicsDevice != null)
        this.loadContent();        
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.update = function(gameTime)
{///<summary>Called when the game has determined that game logic needs to be processed. Override this method with game-specific logic. Call base.</summary>
    ///<param name="gameTime" type="ajax.drawing.Timer">Time passed since the last call to update(gameTime).</param>  
    
    // Update any child updateable components
    for (var i = 0; i < this._components.updateableComponents.length; i++)
    {
        var gc = ajax.xna.framework.IUpdateable.cast(this._components.updateableComponents[i]);
        // Only if update is defined and is enabled
        if (ajax.isDefined(gc.update) && gc.get_enabled())
            gc.update(gameTime);            
    }    
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.beginDraw = function()
{///<summary>Starts the drawing of a frame. This method is followed by calls to Game.Draw and Game.EndDraw. Returns true if the frame should be drawn; false otherwise. Call base.</summary>
 ///<returns type="boolean">true if the frame should be drawn; false otherwise.</returns> 
    // Check manager exists?   
    return this._graphicsDeviceManager.beginDraw();
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.draw = function(gameTime)
{///<summary>Called when the game determines it is time to draw a frame. Override this method with game-specific rendering code. Call base.</summary>
 ///<param name="gameTime" type="ajax.drawing.Timer">Time passed since the last call to draw(gameTime).</param>
    
    // Update any child drawable components
    for (var i = 0; i < this._components.drawableComponents.length; i++)
    {
        var gc = ajax.xna.framework.IDrawable.cast(this._components.drawableComponents[i]);
        // only if draw is defined and is visible
        if (ajax.isDefined(gc.draw) && gc.get_visible())
            gc.draw(gameTime);
    }   
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.endDraw = function()
{///<summary>Ends the drawing of a frame. This method is preceeded by calls to Game.Draw and Game.BeginDraw. Call base.</summary>    
    // Check manager exists?
    this._graphicsDeviceManager.endDraw();
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype._drawFrame = function()
{
    if (this.beginDraw())
        this.draw(this.gameTime);
    this.endDraw();
}


//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.tick = function()
{///<summary>Updates the game's clock and calls Game.Update and Game.Draw. Call base.</summary>
    var gameTime = this.gameTime;

    // Don't do anything until we have something to draw on
    if (!this.graphicsDevice)
        return;

    // Time how long this method takes to execute...
    var startTimeMillis = new Date().getTime();

    // Update the timer
    gameTime.iterate();

    // Call our update sequence
    this.update(gameTime);
    this._drawFrame();

    // end timing
    var endTimeMillis = new Date().getTime();

    /*
    // use targetFramerate/15 here as min value as some browser timers are so innacurate they say it took no time at all
    // or even Negative time to execute this method.
    this._loopMilliseconds = Math.max(endTimeMillis - startTimeMillis, this.targetFrameRate/15);
    // If framerate is to be capped, adjust the stepMillis here.
    if (this.targetFrameRate)
    this.loop.stepMillis = Math.max((1000 / this.targetFrameRate) - this.loopMilliseconds, 0);
    
    */    
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.beginRun = function()
{///<summary>Called after all components are initialized but before the first update in the game loop. No need to call base.</summary>
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.run = function()
{///<summary>Call this method to initialize the game, begin running the game loop, 
    ///     and start processing events for the game. Call base.</summary>    
    
    // Assign IGraphicsDeviceManager to use.
    this._graphicsDeviceManager = ajax.xna.framework.IGraphicsDeviceManager.cast(this.services.getService(ajax.xna.framework.IGraphicsDeviceManager.getFunctionName()));
    if (this._graphicsDeviceManager)    
        this._graphicsDeviceManager.createDevice();
    
    // May as well assign graphics Device service too, so we can assign the graphics device to a simple property, just to make the API a little prettier...
    this._graphicsDeviceService = ajax.xna.framework.graphics.IGraphicsDeviceService.cast(this.services.getService(ajax.xna.framework.graphics.IGraphicsDeviceService.getFunctionName()));    
    if (this._graphicsDeviceService)
        this.graphicsDevice = this._graphicsDeviceService.graphicsDevice;
    
    this.initialize();
    this.beginRun();
    this.gameTime.iterate();    
    
    if (!this._gameWindow.getVisible())
        this._gameWindow.showCentered();
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.endRun = function()
{///<summary>Called after the game loop has stopped running before exiting. No need to call base.</summary>
}

//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.dispose = function()
{///<summary>Releases all resources used by the Game class. Call base.</summary>
    ajax.debug.Output.writeLn("Game is disposing.");
    // Dispose of all game components.
    for (var i = 0; i < this._components.get_count(); i++)
        ajax.xna.framework.IGameComponent.cast(this._components[i]).dispose();        
    this._components.clear();

    this._loop.dispose();
    this.services.dispose(); 
    if (this._graphicsDeviceManager)
        this._graphicsDeviceManager.dispose();
    
    ajax.xna.framework.Game.superClass.dispose.call(this);
}



//-----------------------------------------------------------------------------
ajax.xna.framework.Game.prototype.exit = function()
{///<summary>Exits the game.</summary>
    ajax.debug.Output.writeLn("Game is exiting...");
    this._loop.stop();                     
    this.endRun();
    this.dispose();
}


// Statics
//-----------------------------------------------------------------------------
ajax.xna.framework.Game.cast = function(obj)
{///<returns type="ajax.xna.framework.Game"/>
    return obj;
}


